package de.jmda.mview.fx;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javafx.scene.Group;
import javafx.scene.Node;

import org.apache.log4j.Logger;

import de.jmda.mproc.RelationInfoCalculator;
import de.jmda.mproc.RelationInfoCalculator.RelationInfoHas;
import de.jmda.mproc.RelationInfoCalculator.RelationInfoIsA;
import de.jmda.mview.fx.node.typeshape.Typeshape;
import de.jmda.mview.fx.node.typeshape.relation.Relation;
import de.jmda.mview.fx.node.typeshape.relation.Relation.Orthogonality;
import de.jmda.mview.fx.node.typeshape.relation.RelationHas;
import de.jmda.mview.fx.node.typeshape.relation.RelationIsA;

public class MViewGroup
{
	private final static Logger LOGGER = Logger.getLogger(MViewGroup.class);

	private Group group;
	private Map<String, Typeshape> typeshapesByTypeElementString;
	private Set<Relation> relations;

	public MViewGroup()
	{
		super();

		group = new Group();
		group.setLayoutX(0);
		group.setLayoutY(0);
		typeshapesByTypeElementString = new HashMap<String, Typeshape>();
		relations = new HashSet<Relation>();
	}
	
	public Group get()
	{
		return group;
	}

	public void add(Typeshape typeshape)
	{
		String typeElementName = typeshape.getTypeElement().toString();
		
		if (typeshapesByTypeElementString.containsKey(typeElementName))
		{
			LOGGER.warn(
					"shape for type " + typeElementName +
					" is already member of " + getClass().getName());
			return;
		}

		group.getChildren().add(typeshape);

		// handle relations between type(shape)s in group and new type(shape)
		for (Typeshape typeshapeInGroup : typeshapesByTypeElementString.values())
		{
			// has relations from new type(shape) to type(shape)s already in group
			RelationInfoHas[] relationInfosHas =
					RelationInfoCalculator.calculateRelationInfoHas(
							typeshape.getTypeElement(), typeshapeInGroup.getTypeElement());
			
			for (RelationInfoHas relationInfoHas : relationInfosHas)
			{
				add(relationInfoHas, typeshape, typeshapeInGroup);
			}
			
			// has relations from type(shape)s already in group to type(shape)
			relationInfosHas =
					RelationInfoCalculator.calculateRelationInfoHas(
							typeshapeInGroup.getTypeElement(), typeshape.getTypeElement());
			
			for (RelationInfoHas relationInfoHas : relationInfosHas)
			{
				add(relationInfoHas, typeshapeInGroup, typeshape);
			}

			// TODO handle RelationInfoIsA
			// is a relation from new type(shape) to type(shape)s already in group
			RelationInfoIsA relationInfoIsA =
					RelationInfoCalculator.calculateRelationInfoIsA(
							typeshape.getTypeElement(), typeshapeInGroup.getTypeElement());
			
			if (relationInfoIsA != null)
			{
				add(relationInfoIsA, typeshape, typeshapeInGroup);
			}

			// has relations from type(shape)s already in group to type(shape)
			relationInfoIsA =
					RelationInfoCalculator.calculateRelationInfoIsA(
							typeshapeInGroup.getTypeElement(), typeshape.getTypeElement());
			
			if (relationInfoIsA != null)
			{
				add(relationInfoIsA, typeshape, typeshapeInGroup);
			}
		}

		// has relation from type(shape) to type(shape)
		RelationInfoHas[] relationInfosHas =
				RelationInfoCalculator.calculateRelationInfoHas(
						typeshape.getTypeElement(), typeshape.getTypeElement());
		
		for (RelationInfoHas relationInfoHas : relationInfosHas)
		{
			add(relationInfoHas, typeshape, typeshape);
		}

		typeshapesByTypeElementString.put(typeElementName, typeshape);
	}
	
	private void add(
			RelationInfoHas relationInfo, Typeshape source, Typeshape target)
	{
		Relation relation =
				new RelationHas(relationInfo, source, target, Orthogonality.ON);
//		Relation relation =
//				new RelationHas(relationInfo, source, target, Orthogonality.OFF);
		
		for (Node node : relation.getNodes())
		{
			group.getChildren().add(node);
		}
		
		relations.add(relation);
	}
	
	private void add(
			RelationInfoIsA relationInfo, Typeshape source, Typeshape target)
	{
		Relation relation =
				new RelationIsA(relationInfo, source, target, Orthogonality.ON);
		
		for (Node node : relation.getNodes())
		{
			group.getChildren().add(node);
		}
		
		relations.add(relation);
	}
}